package com.qing.qingcodegenerate.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.qing.qingcodegenerate.ai.AiAssistantService;
import com.qing.qingcodegenerate.ai.AiAssistantServiceFactory;
import com.qing.qingcodegenerate.ai.AiCodeGeneratorServiceFactory;
import com.qing.qingcodegenerate.constant.AppConstant;
import com.qing.qingcodegenerate.constant.UserConstant;
import com.qing.qingcodegenerate.core.AiCodeGeneratorFacade;
import com.qing.qingcodegenerate.core.builder.VueProjectBuilder;
import com.qing.qingcodegenerate.core.handler.StreamHandlerExecutor;
import com.qing.qingcodegenerate.exception.BusinessException;
import com.qing.qingcodegenerate.exception.ErrorCode;
import com.qing.qingcodegenerate.exception.ThrowUtils;
import com.qing.qingcodegenerate.langgraph4j.CodeGenWorkflow;
import com.qing.qingcodegenerate.model.dto.app.AppAddRequest;
import com.qing.qingcodegenerate.model.dto.app.AppQueryRequest;
import com.qing.qingcodegenerate.model.entity.App;
import com.qing.qingcodegenerate.mapper.AppMapper;
import com.qing.qingcodegenerate.model.entity.User;
import com.qing.qingcodegenerate.model.enums.CodeGenTypeEnum;
import com.qing.qingcodegenerate.model.enums.MessageTypeEnum;
import com.qing.qingcodegenerate.model.vo.AppVO;
import com.qing.qingcodegenerate.model.vo.UserVO;
import com.qing.qingcodegenerate.service.AppService;
import com.qing.qingcodegenerate.service.ChatHistoryService;
import com.qing.qingcodegenerate.service.ScreenshotService;
import com.qing.qingcodegenerate.service.UserService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;

import java.io.File;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 *  服务层实现。
 *
 * @author 86157
 * @since 2025-08-09
 */
@Slf4j
@Service
public class AppServiceImpl extends ServiceImpl<AppMapper, App>  implements AppService{

    @Resource
    private AiCodeGeneratorFacade aiCodeGeneratorFacade;
    @Resource
    private UserService userService;

    @Resource
    private ChatHistoryService chatHistoryService;
    @Autowired
    private StreamHandlerExecutor streamHandlerExecutor;

    @Resource
    private VueProjectBuilder vueProjectBuilder;

    @Resource
    private ScreenshotService screenshotService;

    @Resource
    private AiAssistantServiceFactory aiAssistantServiceFactory;

    @Value("${code.deploy-host:http://localhost}")
    private String deployHost;






    @Override
    public Flux<String> chatToGenCode(Long appId, String message, User loginUser, boolean agent) {
        // 1. 参数校验
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用 ID 不能为空");
        ThrowUtils.throwIf(StrUtil.isBlank(message), ErrorCode.PARAMS_ERROR, "用户消息不能为空");


        CodeGenTypeEnum codeGenTypeEnum =null;

        // 5. 保存用户消息到聊天历史
        chatHistoryService.addChatHistory(appId, message, MessageTypeEnum.USER.getValue(), loginUser.getId());

        // 6. 根据 agent 参数选择生成方式
        Flux<String> codeStream;
        if (agent) {
            // Agent 模式：使用工作流生成代码
            codeStream = new CodeGenWorkflow().executeWorkflowWithFlux(message, appId);
        } else {
            this.setAppAttr(appId, loginUser);
            // 2. 查询应用信息
            App app = this.getById(appId);
            ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR, "应用不存在");
            // 3. 验证用户是否有权限访问该应用，仅本人可以生成代码
            if (!app.getUserId().equals(loginUser.getId())) {
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限访问该应用");
            }
            // 4. 获取应用的代码生成类型
            String codeGenTypeStr = app.getCodeGenType();
            codeGenTypeEnum = CodeGenTypeEnum.getEnumByValue(codeGenTypeStr);

            // 传统模式：调用 AI 生成代码（流式）
            codeStream = aiCodeGeneratorFacade.generateAndSaveCodeStream(message, codeGenTypeEnum, appId);
        }
        if (codeGenTypeEnum == null) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "不支持的代码生成类型");
        }
        // 7. 保存 AI 回复到聊天历史
        return streamHandlerExecutor.doExecute(codeStream, chatHistoryService, appId, loginUser, codeGenTypeEnum);
    }

    @Override
    public AppVO getAppVO(App app) {
        if (app == null) {
            return null;
        }
        AppVO appVO = new AppVO();
        BeanUtil.copyProperties(app, appVO);
        // 关联查询用户信息
        Long userId = app.getUserId();
        if (userId != null) {
            User user = userService.getById(userId);
            appVO.setUser(UserVO.objToVO(user));
        }
        return appVO;
    }

    @Override
    public QueryWrapper getQueryWrapper(AppQueryRequest appQueryRequest) {
        if (appQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }
        Long id = appQueryRequest.getId();
        String appName = appQueryRequest.getAppName();

        String initPrompt = appQueryRequest.getInitPrompt();
        String codeGenType = appQueryRequest.getCodeGenType();
        String deployKey = appQueryRequest.getDeployKey();
        String appCategory = appQueryRequest.getAppCategory();
        Integer priority = appQueryRequest.getPriority();
        Long userId = appQueryRequest.getUserId();
        String sortField = appQueryRequest.getSortField();
        String sortOrder = appQueryRequest.getSortOrder();

        QueryWrapper queryWrapper = QueryWrapper.create()
                .eq("id", id)
                .like("appName", appName)
                .like("initPrompt", initPrompt)
                .eq("codeGenType", codeGenType)
                .eq("appCategory", appCategory==""?null:appCategory)
                .eq("deployKey", deployKey)
                .eq("priority", priority)
                .eq("userId", userId);


        // 处理排序逻辑
        if (StrUtil.isBlank(sortField)) {
            // 默认按热度（priority）降序排列，热度越高数值越大
            queryWrapper.orderBy("priority", true);
            // 热度相同时按部署时间降序排列
            queryWrapper.orderBy("deployedTime", false);
        } else {
            // 根据指定的排序字段排序
            if ("hot".equals(sortField)) {
                // 按热度排序
                queryWrapper.orderBy("priority", "ascend".equals(sortOrder));
                // 热度相同时按部署时间排序
                queryWrapper.orderBy("deployedTime", "ascend".equals(sortOrder));
            } else if ("latest".equals(sortField)) {
                // 按最新排序（部署时间）- 根据需求，这里使用升序
                queryWrapper.orderBy("deployedTime", true);
            } else {
                // 其他排序字段
                queryWrapper.orderBy(sortField, "ascend".equals(sortOrder));
            }
        }

        return queryWrapper;
    }
    @Override
    public List<AppVO> getAppVOList(List<App> appList) {
        if (CollUtil.isEmpty(appList)) {
            return new ArrayList<>();
        }
        // 批量获取用户信息，避免 N+1 查询问题
        Set<Long> userIds = appList.stream()
                .map(App::getUserId)
                .collect(Collectors.toSet());
        Map<Long, UserVO> userVOMap = userService.listByIds(userIds).stream()
                .collect(Collectors.toMap(User::getId, UserVO::objToVO));
        return appList.stream().map(app -> {
            AppVO appVO = getAppVO(app);
            UserVO userVO = userVOMap.get(app.getUserId());
            appVO.setUser(userVO);
            return appVO;
        }).collect(Collectors.toList());
    }

    @Override
    public String deployApp(Long appId, User loginUser) {
        // 1. 参数校验
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用 ID 不能为空");
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR, "用户未登录");
        // 2. 查询应用信息
        App app = this.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR, "应用不存在");
        // 3. 验证用户是否有权限部署该应用，仅本人可以部署
        if (!app.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限部署该应用");
        }
        // 4. 检查是否已有 deployKey
        String deployKey = app.getDeployKey();
        // 没有则生成 6 位 deployKey（大小写字母 + 数字）
        if (StrUtil.isBlank(deployKey)) {
            deployKey = RandomUtil.randomString(6);
        }
        // 5. 获取代码生成类型，构建源目录路径
        String codeGenType = app.getCodeGenType();
        String sourceDirName = codeGenType + "_" + appId;
        String sourceDirPath = AppConstant.CODE_OUTPUT_ROOT_DIR + File.separator + sourceDirName;
        // 6. 检查源目录是否存在
        File sourceDir = new File(sourceDirPath);
        if (!sourceDir.exists() || !sourceDir.isDirectory()) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "应用代码不存在，请先生成代码");
        }
// 7. Vue 项目特殊处理：执行构建
        CodeGenTypeEnum codeGenTypeEnum = CodeGenTypeEnum.getEnumByValue(codeGenType);
        if (codeGenTypeEnum == CodeGenTypeEnum.VUE_PROJECT) {
            // Vue 项目需要构建
            boolean buildSuccess = vueProjectBuilder.buildProject(sourceDirPath);
            ThrowUtils.throwIf(!buildSuccess, ErrorCode.SYSTEM_ERROR, "Vue 项目构建失败，请检查代码和依赖");
            // 检查 dist 目录是否存在
            File distDir = new File(sourceDirPath, "dist");
            ThrowUtils.throwIf(!distDir.exists(), ErrorCode.SYSTEM_ERROR, "Vue 项目构建完成但未生成 dist 目录");
            // 将 dist 目录作为部署源
            sourceDir = distDir;
            log.info("Vue 项目构建成功，将部署 dist 目录: {}", distDir.getAbsolutePath());
        }
        // 8. 复制文件到部署目录
        String deployDirPath = AppConstant.CODE_DEPLOY_ROOT_DIR + File.separator + deployKey;
        try {
            FileUtil.copyContent(sourceDir, new File(deployDirPath), true);
        } catch (Exception e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "部署失败：" + e.getMessage());
        }
        // 9. 更新应用的 deployKey 和部署时间
        App updateApp = new App();
        updateApp.setId(appId);
        updateApp.setDeployKey(deployKey);
        updateApp.setDeployedTime(LocalDateTime.now());

        boolean updateResult = this.updateById(updateApp);
        ThrowUtils.throwIf(!updateResult, ErrorCode.OPERATION_ERROR, "更新应用部署信息失败");
        // 10. 构建应用访问 URL
        String appDeployUrl = String.format("%s/%s/", deployHost, deployKey);
        // 11. 异步生成截图并更新应用封面
        generateAppScreenshotAsync(appId, appDeployUrl);
        return appDeployUrl;


    }
    /**
     * 异步生成应用截图并更新封面
     *
     * @param appId  应用ID
     * @param appUrl 应用访问URL
     */
    @Override
    public void generateAppScreenshotAsync(Long appId, String appUrl) {
        // 使用虚拟线程异步执行
        Thread.startVirtualThread(() -> {
            // 调用截图服务生成截图并上传
            String screenshotUrl = screenshotService.generateAndUploadScreenshot(appUrl);
            // 更新应用封面字段
            App updateApp = new App();
            updateApp.setId(appId);
            updateApp.setCover(screenshotUrl);
            boolean updated = this.updateById(updateApp);
            ThrowUtils.throwIf(!updated, ErrorCode.OPERATION_ERROR, "更新应用封面字段失败");
        });
    }

    @Override
    public Long createApp(AppAddRequest appAddRequest, User loginUser) {
        // 参数校验
        String initPrompt = appAddRequest.getInitPrompt();
        ThrowUtils.throwIf(StrUtil.isBlank(initPrompt), ErrorCode.PARAMS_ERROR, "初始化 prompt 不能为空");
        // 构造入库对象
        App app = new App();
        BeanUtil.copyProperties(appAddRequest, app);
        app.setUserId(loginUser.getId());

        // 设置创建时间
        app.setCreateTime(LocalDateTime.now());
        // 设置更新时间
        app.setUpdateTime(LocalDateTime.now());
        // 设置编辑时间
        app.setEditTime(LocalDateTime.now());
        // 插入数据库
        boolean result = this.save(app);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        return app.getId();
    }

    @Override
    @Transactional
    public boolean deleteApp(Long appId, User loginUser) {
        // 1. 参数校验
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用 ID 不能为空");
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR, "用户未登录");
        // 2. 查询应用信息
        App app = this.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR, "应用不存在");
        // 3. 验证用户是否有权限删除该应用，仅本人或管理员可以删除
        if (!app.getUserId().equals(loginUser.getId()) && !UserConstant.ADMIN_ROLE.equals(loginUser.getUserRole())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限删除该应用");
        }
        // 4. 删除关联的聊天历史
        chatHistoryService.deleteByAppId(appId);
        // 5. 删除应用
        boolean result = this.removeById(appId);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "删除应用失败");
        return result;
    }

    @Override
    public void setAppAttr(Long appId, User loginUser) {

        App app = this.getById(appId);
        // 使用 AI 智能选择代码生成类型和名称
        // 新增存在性检查
        if (StrUtil.isNotBlank(app.getAppName()) && StrUtil.isNotBlank(app.getCodeGenType())) {
            return;
        }
        AiAssistantService aiAssistantService = aiAssistantServiceFactory.createAiCodeGenTypeRoutingService();

        Map<String, String> aiResult = aiAssistantService.routeCodeGenType(app.getInitPrompt());
        ThrowUtils.throwIf(aiResult == null, ErrorCode.SYSTEM_ERROR, "AI 选择失败，请重试");

        // 设置应用名称
        String appName = aiResult.get("AppName");
        app.setAppName(appName);

        // 设置代码生成类型
        CodeGenTypeEnum selectedCodeGenType = CodeGenTypeEnum.valueOf(aiResult.get("AppType"));
        app.setCodeGenType(selectedCodeGenType.getValue());

        String appCategory = aiResult.get("AppCategory");
        app.setAppCategory(appCategory);
        this.updateById(app);
    }

    @Override
    public Flux<String> optimizePrompt(String originalPrompt) {
        // 1. 参数校验
        ThrowUtils.throwIf(StrUtil.isBlank(originalPrompt), ErrorCode.PARAMS_ERROR, "原始提示词不能为空");

        AiAssistantService aiAssistantService = aiAssistantServiceFactory.createAiCodeGenTypeRoutingService();

        // 2. 获取专门用于提示词优化的AI服务实例（无记忆，支持多用户并发）
       return aiAssistantService.optimizePromptStream(originalPrompt);
    }

}
